home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / DISK / S14.ARJ / S.ASM next >
Assembly Source File  |  1992-01-05  |  31KB  |  1,216 lines

  1. ;TASM source for S.COM v.1.4.  Copyright (C) DA Nye, 1992, all rights reserved.
  2.  
  3. ideal
  4. model tiny
  5.  
  6. NFILES          EQU 200         ;Max # files in display
  7.  
  8. FILESPEC        EQU 82h         ;Address of filespec in command tail
  9. PATH            EQU 81h         ;Count of chars in path part of filespec here
  10. NORMAL_FILE     EQU 11100001b   ;Attribute for a normal file
  11. TAGGED          EQU 80h         ;Bit to set in attrib to indicate file tagged
  12. DTA_ATTRIB      EQU DTA+21      ;Offsets of elements in DTA
  13. DTA_TIME        EQU DTA+22
  14. DTA_DATE        EQU DTA+24
  15. DTA_SIZE        EQU DTA+26
  16. DTA_NAME        EQU DTA+30
  17. FILE_TIME       EQU bp          ;Addrs of elements in 'fileRecords' after name
  18. FILE_DATE       EQU bp+2
  19. FILE_SIZE       EQU bp+4
  20. INVERSE_VIDEO   EQU 70h         ;Black on white
  21. NORMAL_VIDEO    EQU 17h         ;White on blue
  22. COPYVAL         EQU 1           ;Keys for Copy/Move/Delete/Rename operations
  23. DELETEVAL       EQU 2
  24. MOVEVAL         EQU 3
  25. RENAMEVAL       EQU 4
  26.  
  27. codeseg
  28. BOF:
  29. org 100h
  30.  
  31. Start:
  32.     mov sp, OFFSET stackEnd     ;Initialize stack
  33.     sub ax, ax
  34.     push ax
  35.     mov [programSeg], cs        ;Save current value of CS -> program segment
  36.     mov ah, 0Fh                 ;Find display memory
  37.     int 10h
  38.     mov di, 0B000h              ;If mode 7 (= MDA or Herc), video seg=B000h,
  39.     cmp al, 7
  40.     je @@L1
  41.     mov di, 0B800h              ;Otherwise video seg=B800h,
  42. @@L1:
  43.     mov es, di                  ;Unless running under DesqView
  44.     mov cx, 'DE'
  45.     mov dx, 'SQ'
  46.     mov ax, 2B01h
  47.     int 21h
  48.     cmp al, 0FFh
  49.     je @@L2
  50.     mov ah, 0FEh
  51.     int 10h
  52.     mov di, es
  53. @@L2:
  54.     mov [displaySegment], di
  55.     mov es, [2Ch]               ;Find COMSPEC
  56.     sub di, di
  57. @@L3:
  58.     mov si, OFFSET comspec
  59.     mov cx, 8
  60.     repe cmpsb
  61.     jne @@L3
  62.     mov [comspecOff], di
  63.     mov [comspecSeg], es
  64.     mov es, [programSeg]
  65. NewSpec:
  66.     mov si, FILESPEC            ;Examine command line filespec
  67.     mov di, si
  68.     cmp [byte FILESPEC-2], 1    ;If none, use "*.*"
  69.     jbe @@L3
  70. @@L1:
  71.     lodsb
  72.     cmp al, '*'                 ;Look for wildcard chars
  73.     je GetPathLength
  74.     cmp al, '?'
  75.     je GetPathLength
  76.     cmp al, 0Dh
  77.     jne @@L1
  78.     cmp [byte si-2], '\'        ;If none, assume spec is a directory
  79.     jne @@L2
  80.     dec si                      ;If last char was not '\', add it
  81.     jmp SHORT @@L3
  82. @@L2:
  83.     mov [byte si-1], '\'
  84. @@L3:
  85.     mov ax, si                  ;Compute length of path minus filename
  86.     sub ax, di
  87.     mov [PATH], al
  88.     mov di, si
  89. NewDir:
  90.     mov [keepSorted], 0         ;Directory is initially unsorted
  91.     mov si, OFFSET starDotStar  ;Append '*.*',0
  92.     call CopyString
  93.     jmp SHORT Restart
  94. NoRoomError:
  95.     mov dx, OFFSET noRoomMsg    ;Not enough room to run S
  96. Abort:
  97.     call BlankScreen
  98.     mov ah, 9
  99.     int 21h
  100.     mov ax, 4C00h               ;Bye
  101.     int 21h
  102.  
  103. GetPathLength:
  104.     mov al, [byte FILESPEC-2]   ;Null-terminate other filespecs
  105.     sub ah, ah
  106.     mov si, ax
  107.     add si, FILESPEC-1
  108.     mov [si], ah
  109.     mov cx, ax                  ;Count chars in path excluding file name:
  110.     std                         ;Search backward for last '\' or ':'
  111. @@L1:
  112.     lodsb
  113.     cmp al, '\'
  114.     je @@L2
  115.     cmp al, ':'
  116.     loopne @@L1
  117. @@L2:
  118.     cld
  119.     mov [PATH], cl
  120.  
  121. Restart:
  122.     mov ax, OFFSET files        ;Cursor -> first file
  123.     mov [cursor], ax
  124.     mov [top], ax
  125. Restart0:
  126.     mov bx, (EOF-BOF)/16+1      ;Resize memory to amount needed
  127.     mov ah, 4Ah
  128.     int 21h
  129.     jc NoRoomError
  130.     mov ah, 19h                 ;Get default drive for path/spec display
  131.     int 21h
  132.     add al, 'A'
  133.     mov di, OFFSET pathNSpec    ;Store letter, ':\'
  134.     stosb
  135.     mov ax, '\:'
  136.     stosw
  137.     mov si, di                  ;Get path and name of current directory
  138.     sub dl, dl
  139.     mov ah, 47h
  140.     int 21h
  141.     sub al, al                  ;Find end
  142.     mov cx, -1
  143.     repne scasb
  144.     dec di
  145.     mov ax, ' '+100h*' '        ;Add two spaces
  146.     stosw
  147.     mov si, FILESPEC            ;Append filespec
  148.     call CopyString
  149.  
  150. ;Get all file names matching filespec and set up tables
  151. GetFileRecords:
  152.     mov dx, OFFSET DTA          ;Set up DTA
  153.     mov ah, 1Ah
  154.     int 21h
  155.     sub ax, ax                  ;Initialize file size total
  156.     mov [totalSize], ax
  157.     mov [totalSize+2], ax
  158.     mov dx, FILESPEC            ;Get first file name
  159.     mov cl, 37h
  160.     mov ah, 4Eh
  161.     int 21h
  162.     jnc FileFound               ;No files.  Try a different filespec.
  163.     mov si, OFFSET NoFilesMsg
  164.     call Error
  165.     jmp NewFilespec
  166. FileFound:
  167.     mov di, OFFSET fileRecords  ;DI -> storage for file names
  168.     mov bx, OFFSET files        ;BX -> array of files
  169.     sub bx, 2
  170. StoreFileName:
  171.     add bx, 2                   ;For all files that will fit,
  172.     cmp bx, (OFFSET files) + NFILES*2
  173.     jb @@L1
  174.     sub bx, 2
  175.     mov [last], bx
  176.     mov si, OFFSET tooManyMsg
  177.     jmp DoError
  178. @@L1:
  179.     mov [bx], di                ;Store pointer to status/filename in files[]
  180.     mov al, [DTA_ATTRIB]        ;Store status byte
  181.     and al, 3Fh                 ;Top bit is used to indicate file is marked
  182.     stosb
  183.     mov si, OFFSET DTA_NAME     ;Copy file name from DTA to filename storage
  184.     call CopyString
  185.     inc di
  186.     mov si, OFFSET DTA_TIME     ;Copy time, date and size
  187.     mov cx, 4
  188.     rep movsw
  189.     test [DTA_ATTRIB], 10h      ;If not a subdirectory,
  190.     jnz @@L2
  191.     mov si, OFFSET DTA_SIZE     ;Add size to total
  192.     lodsw
  193.     add [totalSize], ax
  194.     lodsw
  195.     adc [totalSize+2], ax
  196. @@L2:
  197.     mov ah, 4Fh                 ;Next filename
  198.     int 21h
  199.     jnc StoreFileName
  200.     mov [last], bx              ;Save pointer to last file entry
  201.     mov al, [keepSorted]        ;If returning from EXEC, need to resort files?
  202.     or al, al
  203.     jz DisplayFiles
  204.     jmp Sort0
  205.  
  206. ;Main loop.  Display files and wait for command.
  207. DisplayFiles:
  208.     call BlankStatus            ;Clear status line
  209.     mov si, OFFSET helpF1       ;Display help key
  210.     call DisplayString
  211.     mov bx, [cursor]
  212.     mov si, [bx]
  213.     lodsb                       ;Get attributes of file at cursor
  214.     mov [attrib], al            ;Save attribute byte
  215.     call DisplayString          ;Display name of highlighted file
  216.     mov bp, si                  ;Save pointer to time, date, size
  217.     test [attrib], 10h          ;If a directory,
  218.     jz @@L7
  219.     mov si, OFFSET dirMsg       ; show '<DIR>' instead of file size
  220.     add di, 2
  221.     call DisplayString
  222.     jmp SHORT @@L9
  223. @@L7:
  224.     lea si, [FILE_SIZE]         ;File size, right justified
  225.     add di, 14
  226.     call WriteLongDecimal
  227. @@L9:
  228.     add di, 18                  ;File date:
  229.     push di
  230.     sub dx, dx
  231.     mov bx, [FILE_DATE]         ;Year
  232.     mov al, bh
  233.     shr al, 1
  234.     add al, 80
  235.     sub ah, ah
  236.     mov cl, 2
  237.     call WriteDecimal
  238.     mov al, '/'
  239.     stosw
  240.     mov ax, bx                  ;Day
  241.     and ax, 1Fh
  242.     mov cx, 2
  243.     call WriteDecimal
  244.     mov al, '/'
  245.     stosw
  246.     mov ax, bx                  ;Month
  247.     mov cl, 5
  248.     shr ax, cl
  249.     and ax, 0Fh
  250.     mov cl, 2
  251.     call WriteDecimal
  252.     pop di
  253.     add di, 12
  254.     push di
  255.     mov bx, [FILE_TIME]         ;File time:
  256.     mov ax, bx                  ;Minutes
  257.     mov cl, 5
  258.     shr ax, cl
  259.     and ax, 3Fh
  260.     mov cx, 2
  261.     call WriteDecimal
  262.     mov al, ':'
  263.     stosw
  264.     mov al, bh                  ;Hours
  265.     mov cl, 3
  266.     shr al, cl
  267.     sub ah, ah
  268.     sub cl, cl
  269.     call WriteDecimal
  270.     cld
  271.     pop di
  272.     add di, 4
  273.     mov dl, [attrib]            ;Display attribute letters
  274.     test dl, 1                  ;Read-only
  275.     jz @@L3
  276.     mov al, 'R'
  277.     stosw
  278. @@L3:
  279.     test dl, 2                  ;Hidden
  280.     jz @@L4
  281.     mov al, 'H'
  282.     stosw
  283. @@L4:
  284.     test dl, 4                  ;System
  285.     jz @@L5
  286.     mov al, 'S'
  287.     stosw
  288. @@L5:
  289.     test dl, 20h                ;Archive
  290.     jz @@L6
  291.     mov al, 'A'
  292.     stosw
  293. @@L6:
  294.     mov al, 186                 ;Display divider
  295.     stosw
  296.     mov si, OFFSET pathNSpec    ;Display path and filespec
  297.     call DisplayString
  298.     mov di, 158                 ;Display total size of displayed files
  299.     mov si, OFFSET totalSize
  300.     call WriteLongDecimal
  301.     mov bx, [top]
  302.     mov di, 160
  303.     cld
  304. DisplayNext:
  305.     mov ah, NORMAL_VIDEO        ;Set to inverse video if cursor line
  306.     cmp bx, [cursor]
  307.     jne @@L0
  308.     mov ah, INVERSE_VIDEO
  309. @@L0:
  310.     cmp bx, [last]              ;If done with files,
  311.     jle @@L0a
  312.     mov cx, 16                  ;Blank out name area
  313.     jmp SHORT @@L7
  314. @@L0a:
  315.     mov si, [bx]                ;Get table entry for a file
  316.     lodsb                       ;Get status byte
  317.     test al, TAGGED             ;If file has been tagged, display '>'
  318.     mov al, '>'
  319.     jnz @@L1
  320.     mov al, ' '
  321. @@L1:
  322.     stosw
  323.     mov cx, 9                   ;In field of 9,
  324. @@L2:
  325.     lodsb                       ;Display filename up to extension
  326.     cmp al, '.'
  327.     je @@L3
  328.     or al, al
  329.     jz @@L6
  330. @@L2a:
  331.     stosw
  332.     loop @@L2
  333. @@L3:
  334.     cmp cx, 9                   ;Check for special cases of '.', '..'
  335.     je @@L2a
  336.     cmp [byte si-2], '.'
  337.     je @@L2a
  338.     mov al, ' '                 ;Else pad with spaces out to 9 chars
  339.     rep stosw
  340. @@L4:
  341.     mov cx, 6                   ;Display extension in field of 6
  342. @@L5:
  343.     lodsb
  344.     or al, al
  345.     jz @@L7
  346.     stosw
  347.     loop @@L5
  348.     jmp SHORT @@L7
  349. @@L6:
  350.     add cx, 6                   ;Just pad with blanks if no extension
  351. @@L7:
  352.     mov al, ' '
  353.     rep stosw
  354.     cmp di, 4000                ;Stop at screenful
  355.     je GetCommand
  356.     cmp di, 3872
  357.     jb @@L8
  358.     sub di, 3808                ;Next column
  359. @@L8:
  360.     add di, 128                 ;Next row
  361.     add bx, 2
  362.     jmp DisplayNext
  363.  
  364. ;Get command
  365. GetCommand:
  366.     call HideCursor
  367.     mov es, [programSeg]
  368.     mov ah, 8                   ;Get keypress
  369.     sub ch, ch
  370. @@L1:
  371.     inc ch
  372.     int 21h
  373.     or al, al
  374.     jz @@L1
  375.     mov ah, ch                  ;AH = 2 if aux code, 1 if plain ASCII
  376.     call ToUpper
  377.     mov di, OFFSET CommandKeys  ;Look it up, get pointer to routine
  378.     mov cx, NCOMMANDS
  379.     repne scasw
  380.     jne InvalidCommand
  381.     add di, CommandAddrs-CommandKeys-2
  382.     mov bx, [cursor]            ;SI -> file record for highlighted file
  383.     mov si, [bx]
  384.     call ShowCursor
  385.     jmp [word di]               ;Jump to routine
  386. InvalidCommand:
  387.     call Beep
  388.     jmp GetCommand
  389.  
  390. ;********************************* Commands ***********************************
  391.  
  392. Up:
  393.     sub bx, 2
  394.     jmp SHORT NewLine
  395.  
  396. Down:
  397.     add bx, 2
  398.     jmp SHORT NewLine
  399.  
  400. Left:
  401.     sub bx, 48
  402.     jmp SHORT NewLine
  403.  
  404. Right:
  405.     add bx, 48
  406.     jmp SHORT NewLine
  407.  
  408. PageUp:
  409.     sub bx, 238
  410.     jmp SHORT NewLine
  411.  
  412. PageDown:
  413.     add bx, 238
  414.  
  415. NewLine:
  416.     cmp bx, [last]              ;Make sure cursor is still within bounds
  417.     jbe @@L1
  418.     mov bx, [last]
  419. @@L1:
  420.     cmp bx, OFFSET files
  421.     jae @@L2
  422.     mov bx, OFFSET files
  423. @@L2:
  424.     cmp bx, [top]               ;Slide window if off screen
  425.     jae @@L3
  426.     mov [top], bx
  427.     jmp SHORT @@L4
  428. @@L3:
  429.     mov ax, bx
  430.     sub ax, 238
  431.     cmp ax, [top]
  432.     jb @@L4
  433.     mov [top], ax
  434. @@L4:
  435.     mov [cursor], bx
  436.     jmp DisplayFiles
  437.  
  438. Tag:
  439.     test [byte si], NOT NORMAL_FILE  ;Only allow marking of normal files
  440.     jnz @@L1
  441.     xor [byte si], TAGGED
  442. @@L1:
  443.     jmp DisplayFiles
  444.  
  445. Go:
  446.     mov [byte inputString], 0   ;Initialize to no user-entered command tail
  447. Go0:
  448.     lodsb                       ;Get attribute byte
  449.     mov dx, si                  ;Join path and file name
  450.     mov si, PATH
  451.     mov di, OFFSET buffer
  452.     call Join
  453.     test al, 10h                ;If a directory,
  454.     jz DoExec
  455. ChangeDir:
  456.     mov dx, OFFSET buffer
  457.     mov ah, 3Bh                 ;Change to it
  458.     int 21h
  459.     mov [byte PATH], 0          ;No path now
  460.     mov di, FILESPEC            ;Read in contents of new directory
  461.     jmp NewDir
  462. DoExec:
  463.     mov si, dx                  ;Find extension
  464. @@L1:
  465.     lodsb
  466.     cmp al, '.'
  467.     je @@L2
  468.     or al, al
  469.     jne @@L1
  470.     jmp SHORT @@L4
  471. @@L2:
  472.     mov di, OFFSET extensions   ;If .EXE, .COM or .BAT, execute it
  473.     mov dx, si
  474. @@L3:
  475.     mov si, dx
  476.     mov cx, 3
  477.     repe cmpsb
  478.     je @@L5
  479.     sub al, al
  480.     repne scasb
  481.     cmp [byte di], 0
  482.     jne @@L3
  483. @@L4:
  484.     mov si, OFFSET notExecMsg   ;Else error, not an executable file
  485.     jmp DoError
  486. @@L5:
  487.     call BlankScreen
  488.     mov dx, OFFSET buffer       ;If .BAT, need COMMAND.COM, otherwise don't
  489.     cmp [byte di], 0
  490.     jne @@L6
  491.     mov si, OFFSET CC
  492. label DoEdit near               ;Edit function enters here
  493.     mov di, OFFSET EXECCmdLine+1
  494.     call CopyString             ;Store '/c ' or '/c <editor> ' to command tail
  495.     mov dl, cl
  496.     mov si, OFFSET buffer       ;Append path\file
  497.     call CopyString
  498.     add dl, cl
  499.     mov [EXECCmdLine], dl       ;Store length, append CR
  500.     mov [byte di], 13
  501.     mov dx, [comspecOff]
  502.     mov ds, [comspecSeg]
  503. @@L6:
  504.     cmp [byte cs:inputString], 0    ;If a command tail was entered,
  505.     jz DoDOS
  506.     push ds
  507.     mov ds, [cs:programSeg]
  508.     mov di, OFFSET EXECCmdLine+1
  509.     mov al, ' '                 ;Add a space
  510.     stosb
  511.     mov si, OFFSET inputString  ;Add command tail
  512.     call CopyString
  513.     inc cl
  514.     add [EXECCmdLine], cl
  515.     mov [byte di], 13
  516.     pop ds
  517. DoDOS:
  518.     mov cx, bx
  519.     mov bx, (inputString-BOF)/16+1  ;Release unneeded memory
  520.     mov ah, 4Ah
  521.     int 21h
  522.     jc SysErr
  523.     call BlankScreen
  524.     push cx
  525.     mov [cs:temp], sp           ;Do EXEC
  526.     mov bx, OFFSET EXECParams
  527.     mov ax, 4B00h
  528.     int 21h
  529.     mov bx, cs                  ;Restore critical registers
  530.     mov ds, bx
  531.     mov es, bx
  532.     mov ss, bx
  533.     mov sp, [temp]
  534.     pop bx
  535.     jnc @@L2
  536.     mov si, OFFSET sysErrMsg    ;EXEC error
  537.     cmp al, 8                   ;If return code = 8, no room
  538.     jne @@L1
  539.     mov si, OFFSET noExecRoomMsg
  540. @@L1:
  541.     jmp DoError
  542. @@L2:
  543.     mov [byte EXECCmdLine], 0   ;Tidy up
  544.     jmp Restart0
  545.  
  546. SysErr:
  547.     mov si, OFFSET sysErrMsg
  548. DoError:
  549.     call Error
  550.     jmp DisplayFiles
  551.  
  552. GoCL:
  553.     mov si, OFFSET commandTailMsg   ;Prompt for command tail
  554.     call Query
  555.     mov si, [bx]
  556.     jmp Go0
  557.  
  558. DOS:
  559.     call BlankScreen
  560.     mov dx, [comspecOff]
  561.     mov ds, [comspecSeg]
  562.     jmp DoDOS
  563.  
  564. Edit:
  565.     lea dx, [si+1]              ;Join path and file name
  566.     mov si, PATH
  567.     mov di, OFFSET buffer
  568.     call Join
  569.     mov si, OFFSET editor       ;Invoke editor
  570.     mov [byte inputString],0
  571.     jmp DoEdit
  572.  
  573. Copy:
  574.     mov [byte CMDR], COPYVAL    ;Set Copy/Move/Delete/Remove key to Copy
  575.     jmp SHORT DoCopy
  576.  
  577. Delete:
  578.     mov [byte CMDR], DELETEVAL  ;Set Copy/Move/Delete/Remove key to Delete
  579.     jmp SHORT DoCMDR
  580.  
  581. Move:
  582.     mov [byte CMDR], MOVEVAL    ;Set Copy/Move/Delete/Rename key to Move
  583.  
  584. DoCopy:
  585.     mov si, OFFSET DestMsg      ;If Copy or Move, prompt for destination
  586.     call Query
  587.     mov di, si
  588.     mov [byte si], '\'          ;Append '\'
  589.     inc si
  590.     mov [temp], si
  591.     cmp [byte CMDR], MOVEVAL
  592.     jne DoCMDR
  593.     mov ax, [word FILESPEC]     ;If Move to same drive,
  594.     mov dx, [word inputString]
  595.     cmp ah, ':'
  596.     je @@L1
  597.     cmp dh, ':'
  598.     jne @@L2
  599. @@L1:
  600.     cmp ax, dx
  601.     jne DoCMDR
  602. @@L2:
  603.     mov [byte CMDR], RENAMEVAL  ; do Rename instead (much faster)
  604. DoCMDR:
  605.     mov bp, OFFSET files - 2    ;For each file
  606. CMDRNext:
  607.     add bp, 2
  608.     cmp bp, [last]
  609.     jbe @@L0
  610.     jmp GetFileRecords
  611. @@L0:
  612.     mov si, [bp]                ;Skip if not tagged
  613.     lodsb
  614.     test al, TAGGED
  615.     jz CMDRNext
  616.     xor [byte si-1], TAGGED     ;Else untag
  617.     mov dx, si
  618.     mov si, PATH                ;Source path\filename -> sourceFileSpec
  619.     mov di, OFFSET sourceFileSpec
  620.     call Join
  621.     cmp [byte CMDR], DELETEVAL  ;If not Deleting
  622.     je @@L4
  623.     mov si, dx                  ;Append current file's name to destination path
  624.     mov di, [temp]
  625.     call CopyString
  626.     cmp [byte CMDR], RENAMEVAL  ;If Rename, do it
  627.     jne @@L2
  628. label DoRename near
  629.     mov dx, OFFSET sourceFileSpec
  630.     mov di, OFFSET inputString
  631.     mov ah, 56h
  632.     int 21h
  633.     jnc @@L1
  634.     mov dx, di                  ;If rename failed, try deleting target name
  635.     mov ah, 41h
  636.     int 21h
  637.     jnc DoRename                ; and try again
  638.     jmp SHORT CantOpen          ;If delete failed, abort
  639. @@L1:
  640.     jmp CMDRNext
  641. @@L2:
  642.     mov dx, OFFSET sourceFileSpec   ;Copy or Move: open source, dest files
  643.     mov ax, 3D00h
  644.     int 21h
  645.     jc CantOpen
  646.     mov [sourceHandle], ax
  647.     sub cx, cx
  648.     mov dx, OFFSET inputString
  649.     mov ax, 3C00h
  650.     int 21h
  651.     jc CantOpen
  652.     mov [destHandle], ax
  653. @@L3:
  654.     mov bx, [sourceHandle]      ;Read a bufferful
  655.     mov cx, 512
  656.     mov dx, OFFSET buffer
  657.     mov ah, 3Fh
  658.     int 21h
  659.     jc ReadError
  660.     mov bx, [destHandle]        ;Write it
  661.     mov cx, ax
  662.     mov ah, 40h
  663.     int 21h
  664.     jc WriteError
  665.     cmp ax, cx
  666.     jb FullError
  667.     cmp cx, 512                 ;Loop until done
  668.     je @@L3
  669.     mov ah, 3Eh                 ;Close files
  670.     mov bx, [sourceHandle]
  671.     int 21h
  672.     mov bx, [destHandle]
  673.     int 21h
  674.     cmp [CMDR], MOVEVAL         ;If Move, now do Delete
  675.     jne @@L1
  676. @@L4:
  677.     mov bx, OFFSET sourceFileSpec   ;Delete file
  678.     mov ah, 41h
  679.     int 21h
  680.     jnc @@L1
  681.  
  682. CantOpen:
  683.     mov si, OFFSET cantOpenMsg
  684.     jmp DoError
  685. ReadError:
  686.     mov si, OFFSET readMsg
  687.     jmp DoError
  688. WriteError:
  689.     mov si, OFFSET writeMsg
  690.     jmp DoError
  691. FullError:
  692.     mov si, OFFSET fullMsg
  693.     jmp DoError
  694.  
  695. NewFilespec:
  696.     mov si, OFFSET newSpecMsg   ;Prompt for new filespec
  697.     call Query
  698.     inc al
  699.     mov [FILESPEC-2], al        ;Store count of chars
  700.     mov si, OFFSET inputString
  701.     mov di, FILESPEC
  702.     call CopyString             ;Copy new filespec to command tail area
  703.     mov [byte di], 0Dh          ;Append CR
  704.     jmp NewSpec                 ;Process new filespec
  705.  
  706. Rename:
  707.     lea dx, [si + 1]            ;Join path and current name
  708.     mov si, PATH
  709.     mov di, OFFSET sourceFileSpec
  710.     call Join
  711.     mov si, OFFSET newNameMsg   ;Prompt for new name of file or directory
  712.     call Query
  713.     jmp DoRename
  714.  
  715. Drive:
  716.     mov si, OFFSET newDriveMsg  ;Prompt for letter of drive to change to
  717.     call QueryChar
  718.     sub al, 'A'
  719.     mov dl, al
  720.     mov ah, 0Eh
  721.     int 21h
  722.     cmp dl, al                  ;Ask again if that drive doesn't exist
  723.     jb @@L1
  724.     mov si, OFFSET badDriveMsg
  725.     jmp DoError
  726. @@L1:
  727.     mov [byte FILESPEC-2], 0    ;If successful, start with default filespec
  728.     jmp NewSpec
  729.  
  730. Sort:
  731. ;
  732. ;Sort algorithm:
  733. ; 1) make up array of records {pointer to field to sort | tag}, one for each
  734. ;    file, in 'buffer'.
  735. ; 2) bubble-sort these records
  736. ; 3) copy file record pointers in 'files' to 'buffer' in order of sorted tags
  737. ; 4) copy file record pointers back to 'files' in new order
  738. ;
  739.     mov si, OFFSET sortMsg      ;Prompt for sort field
  740.     call QueryChar
  741.     cmp al, 'N'                 ;Check for legal sort field option
  742.     je Sort0
  743.     cmp al, 'E'
  744.     je Sort0
  745.     cmp al, 'D'
  746.     je Sort0
  747.     mov si, OFFSET genErrorMsg
  748.     jmp DoError
  749. Sort0:
  750.     mov [keepSorted], al        ;Remember for later resorting after EXEC
  751.     mov dl, al
  752.     sub dh, dh                  ;DH = tag (position of file in current order)
  753.     mov di, OFFSET buffer
  754.     mov bx, OFFSET files
  755. @@L1:
  756.     mov si, [bx]                ;Find field to sort:  get pointer to record
  757. @@L1a:
  758.     inc si
  759.     mov cx, si
  760.     cmp dl, 'N'                 ;If Name, already pointing at it
  761.     je @@L4
  762.     sub ah, ah                  ;If Date, find null at end of name
  763.     cmp dl, 'E'                 ;If Ext find '.' or end of name
  764.     jne @@L2
  765.     cmp [byte si], '.'          ;'.' and '..' are special cases
  766.     je @@L1a
  767.     mov ah, '.'
  768. @@L2:
  769.     lodsb
  770.     or al, al
  771.     je @@L3
  772.     cmp al, ah
  773.     jne @@L2
  774. @@L3:
  775.     dec si                      ;If Ext, back up to '.' or null
  776.     cmp dl, 'E'
  777.     je @@L4
  778.     add si, 3                   ;If Date, advance to date field
  779. @@L4:
  780.     mov ax, si                  ;Store pointer to field to sort
  781.     stosw
  782.     mov al, dh                  ;Store tag
  783.     stosb
  784.     inc dh                      ;Bump tag
  785.     add bx, 2                   ;Loop until no more files
  786.     cmp bx, [last]
  787.     jbe @@L1
  788. DoSort:
  789.     lea bp, [di-3]              ;BP -> last
  790.     push bp
  791. @@L0:
  792.     mov bx, OFFSET buffer       ;Do bubble sort
  793. @@L1:
  794.     mov si, [bx]
  795.     cmp [word si-1], '.'        ;Leave '.' and '..' alone
  796.     je @@L4
  797.     mov di, [bx+3]
  798.     cmp dl, 'D'                 ;If sorting Dates, compare one word
  799.     jne @@L2
  800.     cmpsw
  801.     jmp SHORT @@L3
  802. @@L2:
  803.     mov cx, -1                  ;Else compare bytes until not equal
  804.     repe cmpsb
  805. @@L3:
  806.     jbe @@L4                    ;If first field > second
  807.     mov ax, [bx]                ;Exchange field pointers and tags
  808.     xchg ax, [bx+3]
  809.     mov [bx], ax
  810.     mov al, [bx+2]
  811.     xchg al, [bx+5]
  812.     mov [bx+2], al
  813. @@L4:
  814.     add bx, 3                   ;Loop until no more files this pass
  815.     cmp bx, bp
  816.     jb @@L1
  817.     sub bp, 3
  818.     cmp bp, OFFSET buffer       ;Loop until no more passes
  819.     jne @@L0
  820. OrderByTags:
  821.     mov di, OFFSET buffer       ;Arrange file pointers in order of tags
  822.     mov si, OFFSET files
  823.     pop bp
  824. @@L1:
  825.     mov bl, [di+2]              ;Get tag
  826.     sub bh, bh
  827.     add bx, bx
  828.     mov ax, [bx+si]             ;Get file ptr associated with that tag
  829.     stosw                       ;Store in place of field pointer in sort buffer
  830.     inc di
  831.     cmp di, bp
  832.     jbe @@L1
  833.     mov si, OFFSET buffer
  834.     mov di, OFFSET files
  835. @@L2:
  836.     movsw                       ;Copy file pointers back in new order
  837.     inc si
  838.     cmp di, [last]
  839.     jbe @@L2
  840.     jmp DisplayFiles
  841.  
  842. Help:
  843.     call BlankStatus            ;Display help on status line
  844.     mov si, OFFSET helpMsg
  845.     call DisplayString
  846.     mov ah, 8
  847.     int 21h
  848.     jmp DisplayFiles
  849.  
  850. Exit:
  851.     call BlankScreen
  852.     mov ax, 4C00h               ;Bye
  853.     int 21h
  854.  
  855. ;******************************* Subroutines **********************************
  856.  
  857. WriteLongDecimal:
  858. ;
  859. ;Display 4-byte integer
  860. ;  IN: SI -> number, DI -> display address of least significant digit
  861. ; OUT: none
  862. ;USED: AX CX DX
  863.     push cx
  864.     push dx
  865.     push di
  866.     mov ax, [si]
  867.     mov dx, [si + 2]
  868.     mov cx, 10000               ;Divide by 10000, show quotient|remainder
  869.     div cx
  870.     xchg ax, dx
  871.     or dx, dx
  872.     jz @@L1
  873.     mov cl, 4
  874.     call WriteDecimal
  875.     mov ax, dx
  876. @@L1:
  877.     sub cl, cl
  878.     call WriteDecimal
  879.     pop di
  880.     pop dx
  881.     pop cx
  882.     ret
  883.  
  884. WriteDecimal:
  885. ;
  886. ;Display a decimal number in inverse video, writing digits backwards from right.
  887. ;  IN: AX = number, CL = field width with leading 0s (no leading 0s if CL = 0)
  888. ;      ES:DI -> Video RAM where rightmost digit will go
  889. ; OUT: AH = INVERSE_VIDEO, DI -> left of first digit
  890. ;USED: AL, CX
  891.     push bx
  892.     push dx
  893.     std
  894.     mov bx, 10
  895.     sub ch, ch
  896. @@L1:
  897.     sub dx, dx                  ;Get a digit
  898.     div bx
  899.     xchg ax, dx                 ;Write it to display, right to left
  900.     add ax, '0' + INVERSE_VIDEO*100h
  901.     stosw
  902.     xchg ax, dx
  903.     cmp cx, 0                   ;If CX > 0, loop even if AX = 0
  904.     jg @@L2
  905.     or ax, ax                   ;Else loop only if AX > 0 (more digits left)
  906.     jz @@L3
  907. @@L2:
  908.     loop @@L1
  909. @@L3:
  910.     pop dx
  911.     pop bx
  912.     mov ah, INVERSE_VIDEO
  913. Ret1:
  914.     ret
  915.  
  916. DisplayString:
  917. ;
  918. ;Display string on status line.
  919. ;  IN: SI -> string (null-terminated), ES:DI -> status line, AH = attribute
  920. ; OUT: DI is advanced past end of string
  921. ;USES: AL SI
  922. @@L1:
  923.     lodsb
  924.     or al, al
  925.     jz Ret1
  926.     stosw
  927.     jmp @@L1
  928.  
  929. DisplayInputString:
  930. ;
  931. ;Same as above but show cursor at end of line
  932. ;  IN, OUT, USES -- see above
  933.     push bx
  934.     push dx
  935.     call DisplayString
  936.     mov dx, di                  ;Set cursor to end of printed string
  937.     add dx, 2
  938.     shr dl, 1
  939.     sub dh, dh
  940.     mov ah, 2
  941.     sub bx, bx
  942.     int 10h
  943.     pop dx
  944.     pop bx
  945.     ret
  946.  
  947.  
  948. Error:
  949. ;
  950. ;Beep, display string on status line and wait for keypress (any key)
  951. ;  IN: SI -> string
  952. ; OUT: ES -> program segment
  953. ;USED: AX
  954.     call Beep
  955.     call BlankStatus
  956.     call DisplayString          ;Display error string
  957.     mov si, OFFSET ErrorMsg     ;Display 'Press any key' message
  958.     call DisplayString
  959.     mov ah, 8
  960.     int 21h
  961.     mov es, [programSeg]
  962.     ret
  963.  
  964. Query:
  965. ;
  966. ;Prompt for string input on status line
  967. ;  IN: SI -> message
  968. ; OUT: SI -> null at end of ASCIIZ string input, AX = length (excluding null)
  969. ;USED: none
  970.     push bx
  971.     push cx
  972.     push dx
  973.     push di
  974.     push es
  975.     call BlankStatus
  976.     call DisplayInputString
  977.     mov cx, 80                  ;Get input
  978.     mov dx, OFFSET inputString
  979.     mov si, dx
  980.     sub bx, bx
  981.     mov ah, 3Fh
  982.     int 21h
  983.     sub ax, 2
  984.     add si, ax
  985.     mov [byte si], 0            ;Null-terminate it
  986.     pop es
  987.     pop di
  988.     pop dx
  989.     pop cx
  990.     pop bx
  991.     ret
  992.  
  993. QueryChar:
  994. ;
  995. ;Prompt for single character input
  996. ;  IN: SI -> message
  997. ; OUT: AL = character (lower case converted to upper)
  998. ;USED: AH
  999.     push es
  1000.     call BlankStatus
  1001.     call DisplayInputString     ;Display string
  1002.     mov ah, 8                   ;Get char
  1003.     int 21h
  1004.     call ToUpper
  1005.     pop es
  1006.     ret
  1007.  
  1008. BlankStatus:
  1009. ;
  1010. ;Clear top line of display to inverse video
  1011. ;  IN: none
  1012. ; OUT: ES = video segment, DI = 0
  1013. ;USED: AX CX
  1014.     mov ax, [displaySegment]
  1015.     mov es, ax
  1016.     sub di, di
  1017.     mov ax, ' ' + INVERSE_VIDEO*100h
  1018.     mov cx, 80
  1019.     rep stosw
  1020.     sub di, di
  1021.     ret
  1022.  
  1023. Join:
  1024. ;
  1025. ;Copy counted path, then ASCIIZ file name to buffer and null-terminate
  1026. ;  IN: SI -> path, DX -> file, DI -> destination
  1027. ; OUT: DI -> null at end of copied string, CX = total chars excluding null
  1028. ;USED: none
  1029.     push ax
  1030.     push si
  1031.     lodsb                       ;Get count of chars in path (a counted string)
  1032.     sub ah, ah
  1033.     mov cx, ax
  1034.     rep movsb                   ;Copy it to destination
  1035.     mov cx, ax
  1036.     mov si, dx                  ;Now copy file name (null-terminated)
  1037.     call CopyString
  1038.     add cx, ax                  ;Sum string counts -> CX
  1039.     mov [byte di], 0            ;Null-terminate the result
  1040.     pop si
  1041.     pop ax
  1042.     ret
  1043.  
  1044. CopyString:
  1045. ;
  1046. ;Copy null-terminated string.
  1047. ;  IN: SI -> string, DI -> destination
  1048. ; OUT: CX = length (excluding null), DI -> terminating null of copied string
  1049. ;USED: AL
  1050.     mov cx, -1
  1051. @@L1:
  1052.     lodsb                       ;Copy string
  1053.     stosb
  1054.     or al, al                   ;Until null at end is encountered
  1055.     loopnz @@L1                 ;Accumulate count of chars
  1056.     neg cx                      ;Adjust count
  1057.     sub cx, 2
  1058.     dec di                      ;DI -> terminating null
  1059.     ret
  1060.  
  1061. HideCursor:
  1062. ;
  1063. ;Move cursor off bottom of screen
  1064. ;  IN: none
  1065. ; OUT: none
  1066. ;USED: AH
  1067.     push bx
  1068.     push dx
  1069.     mov dx, 1900h
  1070. DoCursor:
  1071.     sub bh, bh
  1072.     mov ah, 2
  1073.     int 10h
  1074.     pop dx
  1075.     pop bx
  1076.     ret
  1077.  
  1078. ShowCursor:
  1079. ;
  1080. ;Put cursor back at 0,0
  1081. ;  IN: none
  1082. ; OUT: none
  1083. ;USED: AH
  1084.     push bx
  1085.     push dx
  1086.     sub dx, dx
  1087.     jmp DoCursor
  1088.  
  1089. Beep:
  1090. ;
  1091. ;Output a bell char
  1092. ;  IN: none
  1093. ; OUT: none
  1094. ;USED: AH DL
  1095.     mov dl, 7
  1096.     mov ah, 2
  1097.     int 21h
  1098.     ret
  1099.  
  1100. BlankScreen:
  1101. ;
  1102. ;Clear screen and home cursor
  1103. ;  IN: none
  1104. ; OUT: none
  1105. ;USED: AX
  1106.     push cx
  1107.     push es
  1108.     push di
  1109.     mov es, [cs:displaySegment] ;Blank screen
  1110.     sub di, di
  1111.     mov ax, ' '+NORMAL_VIDEO*100h
  1112.     mov cx, 25 * 80
  1113.     rep stosw
  1114.     pop di
  1115.     pop es
  1116.     pop cx
  1117.     ret
  1118.  
  1119. ToUpper:
  1120. ;
  1121. ;Convert lower to upper case
  1122. ;  IN: AL = char
  1123. ; OUT: AL = char
  1124. ;USED: none
  1125.     cmp al, 'a'
  1126.     jb @@L1
  1127.     cmp al, 'z'
  1128.     ja @@L1
  1129.     add al, 'A'-'a'
  1130. @@L1:
  1131.     ret
  1132.  
  1133. ;********************************** Data **************************************
  1134.  
  1135.  
  1136. ;Command dispatch table:  aux,2 or ASCII,1 paired to command routine addresses
  1137.  
  1138. commandKeys     db 72,2, 80,2, 75,2, 77,2, 73,2, 81,2, 'T',1, 13,1, 'C',1
  1139.                 db 'D',1, 'M',1, 'R',1, 'E',1, 'F',1, 'V',1, 'S',1
  1140.                 db 60,2, 10,1, 59,2, 27,1
  1141. commandAddrs    dw Up, Down, Left, Right, PageUp, PageDown, Tag, Go, Copy
  1142.                 dw Delete, Move, Rename, Edit, NewFilespec, Drive, Sort
  1143.                 dw DOS, GoCL, Help, Exit
  1144. NCOMMANDS       EQU (commandAddrs-commandKeys)/2
  1145.  
  1146.  
  1147. ;Strings
  1148.  
  1149. helpMsg         db 'Copy Delete Edit Filespec Move Ren Sort '
  1150.                 db 'Tag driVe Enter=cd/run F2=DOS Esc=exit',0
  1151. helpF1          db 'Help F1',186,0
  1152. noFilesMsg      db 'No matching files or invalid path',0
  1153. noRoomMsg       db 'Out of room$'
  1154. badPathMsg      db 'Bad path',0
  1155. sysErrMsg       db 'System error',0
  1156. cantOpenMsg     db "Can't open file",0
  1157. writeMsg        db 'Write error',0
  1158. fullMsg         db 'Disk full',0
  1159. readMsg         db 'Read error',0
  1160. notExecMsg      db 'Not a directory or executable file',0
  1161. badDriveMsg     db "Drive doesn't exist",0
  1162. genErrorMsg     db 'Error',0
  1163. tooManyMsg      db 'Too many files',0
  1164. noExecRoomMsg   db 'Not enough memory',0
  1165. ErrorMsg        db '.  Press any key.',0
  1166. destMsg         db 'Where to?',0
  1167. newSpecMsg      db 'New filespec:',0
  1168. newDriveMsg     db 'New drive:',0
  1169. sortMsg         db 'Sort on:  Name Ext Date',0
  1170. newNameMsg      db 'New name:',0
  1171. commandTailMsg  db 'Command tail:',0
  1172. dirMsg          db '<DIR>',0
  1173. extensions      db 'EXECOMBAT',0
  1174. starDotStar     db '*.*',0
  1175. editor          db '/C E ', 12 dup (0)
  1176. cc              db '/C ',0
  1177. comspec         db  'COMSPEC='
  1178.  
  1179.  
  1180. ;EXEC function parameter block
  1181.  
  1182. EXECParams      dw 0
  1183. EXECCmdLineOff  dw OFFSET EXECCmdLine
  1184. programSeg      dw 0
  1185.                 dw -1, -1 , -1 , -1
  1186.  
  1187. EXECCmdLine     db 0, 80 dup (?)
  1188. sourceFileSpec  EQU EXECCmdLine     ;Second use for this space during Copy etc.
  1189.  
  1190.  
  1191. ;Variables, buffers
  1192.  
  1193.                 dw 128 dup (?)  ;Stack (here for protection during EXEC)
  1194. stackEnd:
  1195. keepSorted      db ?            ;Holds sort subcommand char if this dir sorted
  1196. CMDR            db ?            ;Key for Copy/Move/Delete/Rename actions
  1197. attrib          db ?            ;File attribute byte
  1198. totalSize       dw ?, ?         ;Sum of file sizes
  1199. sourceHandle    dw ?            ;Source handle for Copy, etc.
  1200. destHandle      dw ?            ;Destination handle for Copy etc.
  1201. cursor          dw ?            ;Position in 'files' of highlighted file
  1202. last            dw ?            ;Position in 'files' of last file in directory
  1203. top             dw ?            ;Position in 'files' of file at top of screen
  1204. temp            dw ?            ;Holds SP during EXEC, other uses
  1205. comspecSeg      dw ?            ;Segment of environment
  1206. comspecOff      dw ?            ;Offset of 'C:\COMMAND.COM' in environment
  1207. displaySegment  dw ?            ;Segment of display RAM
  1208. pathNSpec       db 80 dup (?)   ;Default path and current filespec for display
  1209. DTA             db 64 dup (?)   ;Disk Transfer Area
  1210. inputString     db 80 dup (?)   ;String returned by 'Query', other uses
  1211. files           dw NFILES dup (?)   ;Array of pointers to file records
  1212. fileRecords     db 80*NFILES dup (?)    ;File records: attrib/name/time/date
  1213. buffer          db 512 dup (?)  ;Buffer for Copy, etc.
  1214.  
  1215. EOF:
  1216. end Start